package com.yanti.service.impl;


import com.yanti.domain.dto.PostgraduateRequestDTO;
import com.yanti.domain.enums.AppHttpCodeEnum;
import com.yanti.domain.enums.AssistantType;
import com.yanti.domain.exception.BizException;
import com.yanti.factory.AssistantConfigFactory;
import com.yanti.filter.LoginUserContextHolder;
import com.yanti.repository.ChatHistoryRepository;
import com.yanti.util.MongoChatMemory;
import com.yanti.util.RedisIdWorker;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

import static com.yanti.domain.enums.RedisKeyEnum.CHAT_HISTORY_KEY_PREFIX;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;

@Service
@Slf4j
public class PostgraduateAssistantService {
    private final AssistantConfigFactory configFactory;
    private final ChatClient.Builder clientBuilder;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private MongoTemplate mongoTemplate;

    private final ChatHistoryRepository chatHistoryRepository;

//    private static final String KEY_PREFIX = "chat:history:";

    @Resource
    private RedisIdWorker redisIdWorker;
    // 添加内存监控指标
    public PostgraduateAssistantService(AssistantConfigFactory configFactory,
                                        ChatClient.Builder clientBuilder, MongoTemplate mongoTemplate, ChatHistoryRepository chatHistoryRepository) {
        this.configFactory = configFactory;
        this.clientBuilder = clientBuilder;
        this.mongoTemplate = mongoTemplate;
        this.chatHistoryRepository = chatHistoryRepository;
    }

    public Mono<String> handleRequest(PostgraduateRequestDTO request) {

        return Mono.fromSupplier(() -> {

            String uid = Objects.requireNonNull(LoginUserContextHolder.getUserId()).toString();
            request.setUid(uid);
            String conversationId = request.getConversationId();
            if(StringUtils.isEmpty(conversationId)){
                // 创建新会话
                conversationId = String.valueOf(redisIdWorker.nextId("conversationId"));
                request.setConversationId(conversationId);
                // 保存会话
                chatHistoryRepository.save(request.getAssistantType(), request.getConversationId());
            }else{
                // 如果前端带了conversationId，检查会话是否存在
                String key = CHAT_HISTORY_KEY_PREFIX.getKey() + request.getAssistantType()
                        + ":" + LoginUserContextHolder.getUserId();
                Boolean hasKey = redisTemplate.hasKey(key);
                if(Boolean.FALSE.equals(hasKey)){
                    throw new BizException(AppHttpCodeEnum.CONVERSATION_NOT_EXIST);
                }
            }
            // todo 权限校验

            // 构造MemoryId:确保唯一且稳定
            String memoryId = request.getConversationId() + ":" + LoginUserContextHolder.getUserId();

            // 创建会话内存
            MongoChatMemory chatMemory = new MongoChatMemory(mongoTemplate);
            // 创建会话
            ChatClient client = configFactory.configureAssistant(
                    clientBuilder,
                    AssistantType.valueOf(request.getAssistantType()),
                    chatMemory,
                    memoryId
            );
            log.info("client:{}",client);
            return client.prompt()
                    // 传入对话id
                    .advisors(a->a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, request.getConversationId()))
                    .system(s -> s
                            .param("current_date", LocalDate.now().toString())
                            .param("current_time", LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm")))
                    )
                    .user(request.getMessage())
                    .call()
                    .content();
        }).onErrorResume(e -> Mono.just(e.getMessage()));
    }

    public Mono<String> analysisWrongQuestion(PostgraduateRequestDTO request) {

        return Mono.fromSupplier(() -> {

            String uid = Objects.requireNonNull(LoginUserContextHolder.getUserId()).toString();
            request.setUid(uid);
            String conversationId = request.getConversationId();
            if(StringUtils.isEmpty(conversationId)){
                // 创建新会话
                conversationId = String.valueOf(redisIdWorker.nextId("conversationId"));
                request.setConversationId(conversationId);
                // 保存会话
                chatHistoryRepository.save(request.getAssistantType(), request.getConversationId());
            }else{
                // 如果前端带了conversationId，检查会话是否存在
                String key = CHAT_HISTORY_KEY_PREFIX.getKey() + request.getAssistantType()
                        + ":" + LoginUserContextHolder.getUserId();
                Boolean hasKey = redisTemplate.hasKey(key);
                if(Boolean.FALSE.equals(hasKey)){
                    throw new BizException(AppHttpCodeEnum.CONVERSATION_NOT_EXIST);
                }
            }
            // todo 权限校验

            // 构造MemoryId:确保唯一且稳定
            String memoryId = request.getConversationId() + ":" + LoginUserContextHolder.getUserId();

            // 创建会话内存
            MongoChatMemory chatMemory = new MongoChatMemory(mongoTemplate);
            // 创建会话
            ChatClient client = configFactory.analysisAssistant(
                    clientBuilder,
                    AssistantType.valueOf(request.getAssistantType()),
                    chatMemory,
                    memoryId
            );
            log.info("client:{}",client);
            return client.prompt()
                    // 传入对话id
                    .advisors(a->a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, request.getConversationId()))
                    .system(s -> s
                            .param("current_date", LocalDate.now().toString())
                            .param("current_time", LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm")))
                    )
                    .user(request.getMessage())
                    .call()
                    .content();
        }).onErrorResume(e -> Mono.just(e.getMessage()));
    }
}